package com.hsurosy.hsuchain.nft.datasource.sharding.id;

import cn.hutool.core.util.IdUtil;
import com.hsurosy.hsuchain.nft.datasource.sharding.strategy.DefaultShardingTableStrategy;
import org.apache.commons.lang3.StringUtils;

/**
 * 分布式ID生成器
 *
 * @Author Hsu琛君珩
 * @Date 2024-10-04 19:18
 * @Description
 * @Version: v1.0.0
 */
public class DistributeID {

    /**
     * 系统标识码（业务码）
     */
    private String businessCode;

    /**
     * 表下标，用于分表
     */
    private String table;

    /**
     * 序列号，唯一标识
     */
    private String seq;

    /**
     * 分表策略，使用默认策略
     */
    private static DefaultShardingTableStrategy shardingTableStrategy = new DefaultShardingTableStrategy();

    /**
     * 默认构造函数
     */
    public DistributeID() {
    }

    /**
     * 利用雪花算法生成一个唯一ID
     *
     * @param businessCode 业务码
     * @param workerId 雪花算法的workerId
     * @param externalId 外部ID用于确定分表
     * @return 生成的唯一ID
     */
    public static String generateWithSnowflake(BusinessCode businessCode, long workerId,
                                               String externalId) {
        // 使用雪花算法生成序列号
        long id = IdUtil.getSnowflake(workerId).nextId();
        // 调用generate方法生成ID
        return generate(businessCode, externalId, id);
    }

    /**
     * 生成一个唯一ID
     * 格式：业务码 + 序列号 + 分表编号
     *
     * @param businessCode 业务码
     * @param externalId 外部ID，用于确定分表
     * @param sequenceNumber 序列号
     * @return 生成的唯一ID
     */
    public static String generate(BusinessCode businessCode,
                                  String externalId, Long sequenceNumber) {
        // 创建DistributeID对象
        DistributeID distributeId = create(businessCode, externalId, sequenceNumber);
        // 返回拼接后的业务码、序列号和表下标
        return distributeId.businessCode + distributeId.seq + distributeId.table;
    }

    @Override
    public String toString() {
        // 返回业务码、序列号和表下标的组合字符串
        return this.businessCode + this.seq + this.table;
    }

    /**
     * 创建一个DistributeID对象
     *
     * @param businessCode 业务码
     * @param externalId 外部ID，用于确定分表
     * @param sequenceNumber 序列号
     * @return 创建的DistributeID对象
     */
    public static DistributeID create(BusinessCode businessCode,
                                      String externalId, Long sequenceNumber) {
        // 实例化DistributeID
        DistributeID distributeId = new DistributeID();
        // 设置业务码
        distributeId.businessCode = businessCode.getCodeString();
        // 根据分片策略获取表下标并填充到4位
        String table = String.valueOf(shardingTableStrategy.getTable(externalId, businessCode.tableCount()));
        distributeId.table = StringUtils.leftPad(table, 4, "0");
        // 设置序列号
        distributeId.seq = String.valueOf(sequenceNumber);
        return distributeId;
    }

    /**
     * 获取分片表下标
     *
     * @param distributeId 分布式ID对象
     * @return 表下标
     */
    public static String getShardingTable(DistributeID distributeId){
        return distributeId.table;
    }

    /**
     * 根据外部ID和表数量获取分片表下标
     *
     * @param externalId 外部ID
     * @param tableCount 表数量
     * @return 表下标
     */
    public static String getShardingTable(String externalId, int tableCount) {
        return StringUtils.leftPad(String.valueOf(shardingTableStrategy.getTable(externalId, tableCount)), 4, "0");
    }

    /**
     * 根据ID字符串获取分片表下标
     *
     * @param id 分布式ID字符串
     * @return 表下标
     */
    public static String getShardingTable(String id){
        return getShardingTable(valueOf(id));
    }

    /**
     * 将ID字符串解析为DistributeID对象
     *
     * @param id 分布式ID字符串
     * @return 解析后的DistributeID对象
     */
    public static DistributeID valueOf(String id) {
        DistributeID distributeId = new DistributeID();
        // 提取业务码（前2位）
        distributeId.businessCode = id.substring(0, 2);
        // 提取序列号（中间部分）
        distributeId.seq = id.substring(2, id.length() - 4);
        // 提取表下标（最后4位）
        distributeId.table = id.substring(id.length() - 4, id.length());
        return distributeId;
    }
}
